package com.seed.application.service.get;

import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryTable;
import com.mybatisflex.core.row.DbChain;
import com.mybatisflex.core.row.Row;
import com.seed.core.action.GetAction;
import com.seed.core.column.Column;
import com.seed.core.enumeration.PropertyType;
import com.seed.core.pojo.Const;
import com.seed.core.pojo.GetVO;
import com.seed.core.pojo.seed.BelongsTos;
import com.seed.core.pojo.seed.Habtms;
import com.seed.core.pojo.seed.Seed;
import com.seed.core.query.pojo.Declared;
import com.seed.util.SeedKit;

import java.io.Serializable;
import java.util.*;

public class GetService {

    private final Seed seed;
    private final GetAction action;
    private final String declare;

    public GetService(Seed seed, GetAction action) {
        this.seed = seed;
        this.action = action;
        this.declare = getInferDeclare();
    }

    public GetVO exec(Serializable id) {
        Declared declared = SeedKit.getDeclared(seed, declare);
        PropertyType propertyType = declared.getType();

        return switch (propertyType) {
            case column -> getForColumn(id);
            case hasOne -> getForHasOne(id);
            case belongsTo -> getForBelongsTo(id);
            case hasMany -> getForHasMany(id);
            case habtm -> getForHabtm(id);
            case none, asterisk -> throw new RuntimeException("非法配置");
        };
    }

    public GetVO execBatch(List<Serializable> ids) {
        throw new RuntimeException("TODO");
    }

    private GetVO getForColumn(Serializable id) {
        String tableName = seed.getTableName();
        Column selfNameColumn = SeedKit.getNameColumn(seed);
        Row one =  DbChain.table(tableName)
                .select(Const.ID, declare, selfNameColumn.getName())
                .where(Const.ID + " = ?", id)
                .one();
        if(one == null) throw new RuntimeException("数据不存在");

        return new GetVO((String) id, one.getString(selfNameColumn.getName()), PropertyType.column, one.get(declare));
    }

    private GetVO getForHasOne(Serializable id) {
        throw new RuntimeException("未实现");
    }

    private GetVO getForBelongsTo(Serializable id) {
        String tableName = seed.getTableName();
        Column selfNameColumn = SeedKit.getNameColumn(seed);
        Declared declared = SeedKit.getDeclared(seed, declare);
        BelongsTos belongsTo = (BelongsTos) declared.getProperty();
        String inferSelfColumn = belongsTo.inferSelfColumn();
        String inferTargetColumn = belongsTo.inferTargetColumn();
        Row one = DbChain.table(tableName)
                .select(Const.ID, inferSelfColumn, selfNameColumn.getName())
                .where(Const.ID + " = ?", id)
                .one();
        if(one == null) throw new RuntimeException("数据不存在");

        Object selfValue = one.get(inferSelfColumn);
        if(selfValue == null) return new GetVO((String) id, one.getString(selfNameColumn.getName()), PropertyType.belongsTo);

        Seed targetSeed = belongsTo.targetSeed();
        String targetTableName = targetSeed.getTableName();
        Column targetNameColumn = SeedKit.getNameColumn(targetSeed);
        Row target = DbChain.table(targetTableName)
                .select(inferTargetColumn, targetNameColumn.getName())
                .where(new QueryColumn(new QueryTable(targetTableName), inferTargetColumn).eq(selfValue))
                .one();
        if(target == null) throw new RuntimeException("引用的对象不存在");

        String targetName = target.getString(targetNameColumn.getName());
        return new GetVO((String) id, one.getString(selfNameColumn.getName()), PropertyType.belongsTo, selfValue, targetName);
    }

    private GetVO getForHasMany(Serializable id) {
        throw new RuntimeException("未实现");
    }

    private GetVO getForHabtm(Serializable id) {
        String tableName = seed.getTableName();
        Column selfNameColumn = SeedKit.getNameColumn(seed);
        Declared declared = SeedKit.getDeclared(seed, declare);
        Habtms habtm = (Habtms) declared.getProperty();
        String inferSelfColumn = habtm.inferSelfColumn();
        String inferThroughSelfColumn = habtm.inferThroughSelfColumn();
        String inferThroughTargetColumn = habtm.inferThroughTargetColumn();
        String inferTargetColumn = habtm.inferTargetColumn();
        String throughTableName = habtm.throughTableName();
        Row one = DbChain.table(tableName).select(inferSelfColumn, selfNameColumn.getName()).where(Const.ID + " = ?", id).one();
        if(one == null) throw new RuntimeException("数据不存在");

        Object selfValue = one.get(inferSelfColumn);
        if(selfValue == null) throw new RuntimeException(String.format("【%s】值为空，不合法", inferSelfColumn));

        List<Row> throughList = DbChain.table(throughTableName)
                .select(inferThroughTargetColumn)
                .where(inferThroughSelfColumn + " = ?", selfValue)
                .list();
        Object[] targetIds = throughList.stream().map(e -> e.get(inferThroughTargetColumn)).toArray();

        Seed targetSeed = habtm.targetSeed();
        String targetTableName = targetSeed.getTableName();
        Column nameColumn = SeedKit.getNameColumn(targetSeed);
        List<Row> targetList = targetIds.length > 0
                ? DbChain.table(targetTableName).select(inferTargetColumn, nameColumn.getName())
                .where(new QueryColumn(new QueryTable(targetTableName), inferTargetColumn).in(targetIds))
                .list()
                : Collections.emptyList();

        Map<Object, String> ret = new HashMap<>();
        for(Object targetId: targetIds) {
            Optional<Row> optional = targetList
                    .stream()
                    .filter(e -> targetId.equals(e.get(inferTargetColumn)))
                    .findFirst();
            if(optional.isEmpty()) throw new RuntimeException("引用的对象不存在");

            Row first = optional.get();
            ret.put(targetId, first.getString(nameColumn.getName()));
        }

        return new GetVO((String) id, one.getString(selfNameColumn.getName()), PropertyType.habtm, ret);
    }

    private String getInferDeclare() {
        String declare = action.getDeclare();
        if(StrUtil.isNotBlank(declare)) return StrUtil.toUnderlineCase(declare);

        String actionName = action.getName();
        if(actionName.startsWith("get")) {
            actionName = StrUtil.lowerFirst(actionName.substring(3));
        }

        String columnOrCross = StrUtil.toUnderlineCase(actionName);
        if(seed.getColumns().containsKey(columnOrCross) || seed.getRelations().containsKey(columnOrCross)) {
            return columnOrCross;
        }

        throw new RuntimeException(String.format("请为【%s】配置【declare】", actionName));
    }
}
